
// Main header
#include "Main.h"

// Our header
#include "FArchive.h"

// Fridge headers
#include "SuperTree.h"
#include "FileTools.h"

// Externals
#include <fcntl.h>
#include <io.h>

FArchive::FArchive( const char* szArchivePath )
{
    m_pxPresetArchive = archive_write_new();
    m_xFileInfo = new FFileInfo( szArchivePath, false );
}

FArchive::~FArchive()
{
	archive_write_free( m_pxPresetArchive );
    m_pxPresetArchive = 0;
}

bool FArchive::Extract()
{
    struct archive *pxReadArchive = 0;
    struct archive *pxWriteArchive = 0;
    struct archive_entry *pxArchiveEntry = 0;
    int iFlags = 0;
    int iResult = 0;

    iFlags = ARCHIVE_EXTRACT_TIME;
    iFlags |= ARCHIVE_EXTRACT_PERM;
    iFlags |= ARCHIVE_EXTRACT_ACL;
    iFlags |= ARCHIVE_EXTRACT_FFLAGS;

    pxReadArchive = archive_read_new();
    archive_read_support_format_zip( pxReadArchive );
    archive_read_support_compression_all( pxReadArchive );

    pxWriteArchive = archive_write_disk_new();
    archive_write_disk_set_options( pxWriteArchive, iFlags );
    archive_write_disk_set_standard_lookup( pxWriteArchive );

	iResult = archive_read_open_file( pxReadArchive, m_xFileInfo->GetFullPathAndName(), 10240 );
    if( iResult )
    {
        return false;
    }

    for(;;)
    {
        iResult = archive_read_next_header( pxReadArchive, &pxArchiveEntry);

        // Done
        if( iResult == ARCHIVE_EOF )
        {
            break;
        }

        // Error
        if( iResult != ARCHIVE_OK )
        {
            FridgeLogf( "libarchive error: %s", archive_error_string( pxReadArchive ) );
        }

        if( iResult < ARCHIVE_WARN )
        {
            return false;
        }

        // Set write path to specified directory
        const char* szPath = archive_entry_pathname( pxArchiveEntry );
        char szNewPath[ FRIDGE_MAX_PATH + 1 ];
        char szTempDir[ FRIDGE_MAX_PATH ];
        FileTools::GetTempDirectory( FRIDGE_DIR_TEMPORARY_APP, szTempDir );
        sprintf( szNewPath, "%s/%s", szTempDir, szPath );
        archive_entry_set_pathname( pxArchiveEntry, szNewPath );

        iResult = archive_write_header( pxWriteArchive, pxArchiveEntry );

        if( iResult != ARCHIVE_OK )
        {
            FridgeLogf( "libarchive error: %s", archive_error_string( pxWriteArchive ) );
        }
        else
        {
            const void *pxBuffer;
            size_t uSize;
            long long uOffset;

            // Copy data from archive to disk
            for(;;)
            {
                iResult = archive_read_data_block( pxReadArchive, &pxBuffer, &uSize, &uOffset );

                // We're done
                if( iResult == ARCHIVE_EOF )
                {
                    break;
                }

                // Also done
                if( iResult != ARCHIVE_OK )
                {
                    break;
                }

                iResult = archive_write_data_block( pxWriteArchive, pxBuffer, uSize, uOffset );

                // Error
                if( iResult != ARCHIVE_OK )
                {
                    FridgeLogf( "libarchive error: %s", archive_error_string( pxWriteArchive ) );
                    break;
                }
            }
        }

        iResult = archive_write_finish_entry( pxWriteArchive );

        if( iResult != ARCHIVE_OK )
        {
            FridgeLogf( "libarchive error: %s", archive_error_string( pxWriteArchive ) );
        }

        if( iResult < ARCHIVE_WARN )
        {
            return false;
        }
    }

    // We're done
    archive_read_close( pxReadArchive );
    archive_read_finish( pxReadArchive );
    archive_write_close( pxWriteArchive );
    archive_write_finish( pxWriteArchive );

    return true;
}

void FArchive::WriteToDirectory( const char *szWritePath )
{
    // Set up new zip archive.
    archive_write_set_compression_none( m_pxPresetArchive );
    archive_write_set_format_zip( m_pxPresetArchive );
    archive_write_open_filename( m_pxPresetArchive, szWritePath );

    // Get list of data files
    SuperTree<FFileInfo>* pxTemporaryFileList = new SuperTree<FFileInfo>( true );
    FileTools::FindFiles( pxTemporaryFileList, FRIDGE_FILE_DATA );

    SuperTreeWalker< FFileInfo > xWalker( pxTemporaryFileList );

    // Walk the file tree and add data files
    while( !xWalker.Done() )
    {
        FFileInfo* pxFileInfo = xWalker.Get( true );
        if( pxFileInfo && !pxFileInfo->IsDirectory() )
        {
            AddFile( pxFileInfo->GetFullPathAndName() );
        }
    }

    // We're done
    archive_write_close( m_pxPresetArchive );
    archive_write_finish( m_pxPresetArchive );
}

void FArchive::AddFile( const char* szPath )
{
    #ifdef WIN32
    // TY - I'm not sure what we are going to do about libarchive using oldschool open etc..
    #define open _open
    #define close _close
    #define read _read
    #endif

    struct archive* szArchive;
    struct archive_entry *szArchiveEntry;
    size_t uBytesRead;
    char szDataBuffer[ 8192 ];
    char szCharBuffer[ FRIDGE_MAX_PATH ];
    char* szPathOffset;
    int iFileData;

    szArchive = archive_read_disk_new();
    archive_read_disk_set_standard_lookup( szArchive );

    szArchiveEntry = archive_entry_new();
    iFileData = open( szPath, O_RDONLY );

    // Modify source path to remove temp directory

    char szSourcePath[ FRIDGE_MAX_PATH ];
    strncpy( szSourcePath, szPath, FRIDGE_MAX_PATH );

    // Find temp directory bit
    char szTempDir[ FRIDGE_MAX_PATH ];
    FileTools::GetTempDirectory( FRIDGE_DIR_TEMPORARY_APP, szTempDir );
    szPathOffset = strstr( szSourcePath, szTempDir );
    strncpy( szCharBuffer, szSourcePath, szPathOffset - szSourcePath );
    szCharBuffer[ szPathOffset - szSourcePath ] = 0;

    // Blank out temp directory bit
    sprintf( szCharBuffer + ( szPathOffset - szSourcePath ), "%s%s", "", szPathOffset + strlen( szTempDir ));
    szSourcePath[ 0 ] = 0;
    strcpy( szSourcePath, szCharBuffer );

    // Set correct pathname
    archive_entry_set_pathname( szArchiveEntry, szSourcePath );

    // Write file header to archive
    archive_read_disk_entry_from_file( szArchive, szArchiveEntry, iFileData, NULL );
    archive_write_header( m_pxPresetArchive, szArchiveEntry );

    // Write file data to archive
    while( ( uBytesRead = read( iFileData, szDataBuffer, sizeof( szDataBuffer ) ) ) > 0 )
    {
        archive_write_data( m_pxPresetArchive, szDataBuffer, uBytesRead );
    }

    // We're done
    archive_write_finish_entry( m_pxPresetArchive );
    archive_read_finish( szArchive );
    archive_entry_free( szArchiveEntry );

    close(iFileData);

    #ifdef WIN32
    #undef open
    #undef close
    #undef read
    #endif
}

// eof
